package com.zzyl.service.impl;

import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.zzyl.enums.BasicEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.properties.WechatConfigProperties;
import com.zzyl.service.WechatService;
import com.zzyl.utils.ObjectUtil;
import com.zzyl.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author sjqn
 */
@Service
@Slf4j
public class WechatServiceImpl implements WechatService {


    // 登录
    private static final String GET_OPEN_ID_URL = "https://api.weixin.qq.com/sns/jscode2session?grant_type=authorization_code";

    // 获取access_token
    private static final String GTE_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";

    // 获取手机号
    private static final String GET_POST_PHONE_NUMBER_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=";

    @Autowired
    private WechatConfigProperties wechat;


    /**
     * 获取openid
     *
     * @param code 用户登录时获取的登录凭证
     * @return 返回用户的唯一标识符openid
     * @throws IOException 当网络请求发生错误时抛出
     */
    @Override
    public String getOpenid(String code) {
        // 封装公共参数
        Map<String, Object> paramConfig = getWechatParamConfig();
        paramConfig.put("js_code", code);
        paramConfig.put("grant_type","authorization_code");

        // 发送请求并获取结果token 向指定URL发送GET请求并获取结果
        String jsonStr = HttpUtil.get(GET_OPEN_ID_URL, paramConfig);
        log.info("请求微信接口返回结果：" + jsonStr);

        //json字符转为对象，解析返回的JSON数据
        JSONObject jsonObject = JSONUtil.parseObj(jsonStr);

        //检查是否获取openid，若失败则抛出异常，判断对象是否是一个空的，是空的报错
        if (jsonObject==null) {
            log.error("获取openid失败");
            throw new BaseException(BasicEnum.GET_OPENID_ERROR);
        }
        // 检查是否获取openid，若失败则抛出异常，判断对象是否是一个空的，是空的报错
        String openid = jsonObject.getStr("openid");
        log.info("openid: {}" , openid);
        // 检查是否获取openid，若失败则抛出异常，判断对象是否是一个空的，是空的报错
        if (jsonObject.get("openid") == null) {
            log.error("获取openid失败");
            throw new BaseException(BasicEnum.GET_OPENID_ERROR);
        }

        // 返回openid
        return openid;
    }

    /**
     * 封装公共参数
     * 该方法不接受任何参数，主要用于封装应用程序常用的请求参数，并返回一个包含这些参数的Map对象。
     *
     * @return Map<String, Object> 返回一个包含appid和secret等公共参数的Map对象。
     */
    private Map<String, Object> getWechatParamConfig() {

        // 创建一个空的Map对象用于存放请求参数
        HashMap<String, Object> paramConfig = new HashMap<>();

        // 将应用程序的appid和secret添加到Map中
        paramConfig.put("appid", wechat.getAppid());
        paramConfig.put("secret", wechat.getSecret());

        // 返回包含公共参数的Map对象
        return paramConfig;
    }

    /**
     * 获取手机号
     *
     * @param phoneCode 手机号凭证，用于验证和获取手机号
     * @return 返回经过验证的手机号字符串
     * @throws IOException 如果网络请求发生错误
     */
    @Override
    public String getPhoneNumber(String phoneCode) {

        // 获取access_token，用于后续请求
        String accessToken = getAccessToken();

        // 准备请求参数
        Map<String, Object> param = new HashMap<>();
        param.put("code", phoneCode);

        // 发起POST请求，获取结果
        String jsonStr = HttpUtil.post(GET_POST_PHONE_NUMBER_URL + accessToken, JSONUtil.toJsonStr(param));
        log.info("获取手机号的结果：{}" , jsonStr);

        // 判断结果是否为空，为空则报错
        if (StringUtils.isEmpty(jsonStr)) {
            log.error("获取手机号失败");
            throw new BaseException(BasicEnum.GET_PHONE_ERROR);
        }

        //把字符串转对象
        JSONObject jsonObject = JSONUtil.parseObj(jsonStr);
        //判断对象是否是一个空的，是空的报错
        if (jsonObject == null) {
            log.error("获取手机号失败");
            throw new BaseException(BasicEnum.GET_PHONE_ERROR);
        }
        //判断对象是否是一个空的，是空的报错
        JSONObject phoneInfo = jsonObject.getJSONObject("phone_info");
        if (phoneInfo == null) {
            log.error("获取手机号失败");
            throw new BaseException(BasicEnum.GET_PHONE_ERROR);
        }

        // 从响应中提取并返回手机号
        String phoneNumber = phoneInfo.getStr("phoneNumber");
        log.info("手机号：{}" , phoneNumber);
        return phoneNumber;
    }

    /**
     * 获取访问令牌
     * 该方法不接受任何参数，返回一个字符串类型的访问令牌。
     * 具体流程为：从应用配置中获取请求参数，向指定URL发送GET请求获取令牌字符串。
     * 如果请求返回的code不正确，则抛出运行时异常。
     *
     * @return 返回从服务器获取的访问令牌字符串。
     * @throws RuntimeException 如果获取令牌过程中返回了错误信息，则抛出此异常。
     */
    public String getAccessToken() {
        // 从应用配置中获取请求参数
        Map<String, Object> paramConfig = getWechatParamConfig();
        paramConfig.put("grant_type", "client_credential");

        // 向服务器发送GET请求以获取令牌
        String jsonStr = HttpUtil.get(GTE_ACCESS_TOKEN_URL, paramConfig);
        log.info("请求微信接口返回结果：{}" , jsonStr);
        if (StringUtils.isEmpty(jsonStr)){
            log.error("获取access_token失败");
            throw new BaseException(BasicEnum.GET_TOKEN_ERROR);
        }

        // 解析返回的JSON字符串
        JSONObject jsonObject = JSONUtil.parseObj(jsonStr);

        // 检查是否有错误码，若有则抛出异常
        if (jsonObject.get("access_token") == null) {
            log.error("获取access_token失败");
            throw new BaseException(BasicEnum.GET_TOKEN_ERROR);
        }

        // 返回访问令牌
        String access_token = jsonObject.getStr("access_token");
        log.info("access_token: {}" , access_token);

        return access_token;
    }
}